package gameEngine

import (
	"MetroBuilderGameJam/gameEngine/geom"
	"fmt"
	"github.com/hajimehoshi/ebiten/v2"
	"math"
)

type Camera struct {
	ViewPort   geom.Size
	Position   geom.Point
	ZoomFactor int64
	Rotation   geom.Degrees
}

func (c *Camera) String() string {
	return fmt.Sprintf(
		"T: %.1f, R: %.1f, S: %d",
		c.Position, c.Rotation, c.ZoomFactor,
	)
}

func (c *Camera) viewportCenter() geom.Point {
	return geom.Point{
		c.ViewPort.Width * 0.5,
		c.ViewPort.Height * 0.5,
	}
}

func (c *Camera) worldMatrix() ebiten.GeoM {
	m := ebiten.GeoM{}
	m.Translate(-c.Position.X, -c.Position.Y)
	center := c.viewportCenter()
	// We want to scale and rotate around center of image / screen
	m.Translate(-center.X, -center.Y)
	zoom := math.Pow(1.00001, float64(c.ZoomFactor))
	m.Scale(zoom, zoom)
	m.Rotate(float64(c.Rotation.ToRadians()))
	m.Translate(center.X, center.Y)

	return m
}

func (c *Camera) Render(world, screen *ebiten.Image) {
	screen.DrawImage(world, &ebiten.DrawImageOptions{
		GeoM: c.worldMatrix(),
	})
}

func (c *Camera) ScreenToWorld(pos geom.IntPoint) (geom.Point, error) {
	inverseMatrix := c.worldMatrix()
	if inverseMatrix.IsInvertible() {
		inverseMatrix.Invert()
		posf := pos.AsPoint()
		x, y := inverseMatrix.Apply(posf.X, posf.Y)
		return geom.Point{X: x, Y: y}, nil
	} else {
		return geom.Point{}, fmt.Errorf("matrix is not invertable")
	}
}

func (c *Camera) WorldToScreen(pos geom.Point) geom.IntPoint {
	m := c.worldMatrix()
	x, y := m.Apply(pos.X, pos.Y)
	return geom.IntPoint{X: int(x), Y: int(y)}
}

func (c *Camera) Reset() {
	c.Position.X = 0
	c.Position.Y = 0
	c.Rotation = 0
	c.ZoomFactor = 0
}

func (c *Camera) CenterTo(position *geom.Point) {
	p := c.WorldToScreen(*position)
	c.Position = p.AsPoint().AsVec2().Sub(c.Position.AsVec2().Div(10)).AsPoint()
}
